Skip to content

Agent Health issue for OpenMetrics metrics dropped by configured limit - #24819

Merged
nubtron merged 29 commits into
masterfrom
nubtron/ai-7012-openmetrics-dropped-config
Aug 31, 2026
Merged

Agent Health issue for OpenMetrics metrics dropped by configured limit#24819
nubtron merged 29 commits into
masterfrom
nubtron/ai-7012-openmetrics-dropped-config

Conversation

@nubtron

@nubtron nubtron commented Aug 10, 2026

Copy link
Copy Markdown
Collaborator

What does this PR do?

OpenMetrics stops submitting new metric contexts after it reaches max_returned_metrics. The check logs a warning, but the resulting gaps can look intermittent and are easy to miss.

This PR surfaces that condition in Agent Health. When an OpenMetrics v1 or v2 endpoint exceeds its effective limit, the check reports:

  • Issue name: OpenMetrics Metrics Dropped By Configured Limit
  • Issue type: openmetrics_metrics_dropped_by_configured_limit
  • the affected endpoint, configured limit, observed contexts, dropped contexts, and dropped ratio
  • remediation that starts with filtering unnecessary metrics before suggesting a higher limit, with explicit cost and memory cautions

Remediation shown to customers

The issue carries the following remediation (from metric_limit_issue._remediation):

Summary: Reduce what this endpoint sends to Datadog, or raise this instance's metric limit after checking the cost.

  1. Decide what you actually need. Use metrics / ignore_metrics (OpenMetrics v1) or metrics / exclude_metrics (OpenMetrics v2) on this instance to stop collecting series you do not query, alert on, or keep.
  2. Only then raise max_returned_metrics on this instance to a value above the observed count.
  3. Verify: enable debug_metrics.metric_contexts: true on the instance to publish datadog.agent.metrics.contexts.total and .limit, and confirm the total stays below the limit at peak. Consider a monitor at 80% of the limit.
  4. Check the cost before you leave it: additional contexts are billable custom metrics and increase Agent memory.

Each endpoint gets a stable issue identity derived from the host, check name, endpoint, and namespace. The issue is refreshed while drops continue, resolved on the first clean collection, and reused if the condition later returns.

Severity reflects the fraction of contexts dropped:

  • LOW: less than 5%
  • MEDIUM: 5% to less than 25%
  • HIGH: 25% or more

The Agent Health bridge is best-effort: reporting failures do not fail the check or interfere with the limiter's existing cleanup. The generic AgentCheck hook remains a no-op, so only OpenMetrics v1 and v2 opt into this issue.

The PR also adds the missing issue_type argument to AgentCheck.report_issue. The Agent payload, persistence, and forwarding paths already support this field, so no rtloader or Agent ABI change is needed.

Why?

When OpenMetrics exceeds max_returned_metrics, some metrics are silently discarded after the warning is emitted. Because exporter ordering can change between scrapes, customers may see different metrics disappear over time instead of one obvious hard cutoff.

Agent Health gives this failure mode a durable, endpoint-specific state with actionable remediation, while preserving the existing check lifecycle and limiter behavior.

Review checklist

  • Feature or bugfix has appropriate tests
  • qa/skip-qa is applied
  • Add a backport label if a backport is required

@nubtron nubtron added the qa/skip-qa Automatically skip this PR for the next QA label Aug 10, 2026
@datadog-prod-us1-5

datadog-prod-us1-5 Bot commented Aug 10, 2026

Copy link
Copy Markdown

Pipelines  Tests  Code Coverage

⚠️ Warnings

⚠️ Your PR has warnings. Please review the issues below.

🚦 1 Pipeline job failed

PR All | test / j8a8c654 / Harbor

View more details · View in GitHub Actions

SSLCertVerificationError: certificate verify failed: certificate has expired when making request to API.

🧪 2 Tests failed in 1 job

PR All | run — ❌ 2 tests failed

View more details · View in GitHub Actions

❌ test_check_admin from test_harbor.py   View in Datadog
HTTPSConnectionPool(host='localhost', port=443): Max retries exceeded with url: /api/systeminfo/ (Caused by SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: certificate has expired (_ssl.c:1032)')))
❌ test_check_basic_case from test_harbor.py   View in Datadog
HTTPSConnectionPool(host='localhost', port=443): Max retries exceeded with url: /api/systeminfo/ (Caused by SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: certificate has expired (_ssl.c:1032)')))

ℹ️ Info

No other issues found (see more)

❄️ No new flaky tests detected

🔄 Datadog retried 2 tests - 0 passed on retry View in Datadog

🎯 Code Coverage (details)
Patch Coverage: 98.85%
Overall Coverage: 88.92%

Useful? React with 👍 / 👎

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: d259c32 | Docs | View more details | Give us feedback!

@nubtron nubtron changed the title Prototype: Agent Health issue for OpenMetrics metrics dropped by configured limit Agent Health issue for OpenMetrics metrics dropped by configured limit Aug 10, 2026
nubtron added 17 commits August 10, 2026 18:59
The Fleet UI renders remediation step text as plain text, so the backticks
around config option names were shown literally to customers. Match the
plain-text convention used by other Agent Health producers.
The MetricLimitIssueReporter carried a boolean `legacy` flag whose only
effect was selecting between the v1 (`metrics` / `ignore_metrics`) and
v2 (`metrics` / `exclude_metrics`) config option names in remediation
text. Pass the actual option string instead so the reporter no longer
knows about OpenMetrics flavors and the call sites document themselves.
The Fleet UI renders Agent Health remediation text as plain text, so the
dotted form 'debug_metrics.metric_contexts: true' reads as a single literal
key. The check parses instance.get('debug_metrics', {}).get('metric_contexts'),
so a dotted top-level key would never match and no telemetry would be emitted.

Describe the nesting in prose ('set metric_contexts to true under the
debug_metrics section') and spell out both emitted metric names in full
instead of the orphaned '.limit'. Add assertions locking in the corrected
wording.
@cit-pr-commenter-54b7da

cit-pr-commenter-54b7da Bot commented Aug 24, 2026

Copy link
Copy Markdown

evalya-impact-summary

evalya impact analysis
Impact analysis: RUN-ALL — every test task will run
Trigger:         empty diff (default branch, scheduled run, or shallow-clone fallback)
Test tasks:      0 (all selected)
Publish tasks:   2 (always emitted)
Diff:            empty (no diff information)

Learn more about CI impact filtering

@nubtron
nubtron marked this pull request as ready for review August 24, 2026 13:28
@nubtron
nubtron requested review from a team as code owners August 24, 2026 13:28

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1584239ed8

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread datadog_checks_base/tests/base/checks/openmetrics/test_metric_limit_issue.py Outdated
Comment thread datadog_checks_base/datadog_checks/base/checks/base.py Outdated
Comment thread datadog_checks_base/datadog_checks/base/checks/openmetrics/v2/base.py Outdated
…oints

The V2 metric-limit callback read openmetrics_endpoint from the raw instance,
which stays absent for integrations such as Cilium that synthesize scraper
configs from agent_endpoint/operator_endpoint, so drops were never reported.
Pass the actual configured scraper endpoint keys from self.scrapers instead,
and have V1 pass its one effective prometheus_url.

MetricLimitIssueReporter.handle now accepts a collection of endpoints and
normalizes it to a deterministic, de-duplicated, sorted tuple, discarding
empty values. One aggregate issue is reported for the whole run since the
limiter state cannot attribute drops to a single scraper. The machine-readable
extra['endpoints'] list and the issue identity reflect the endpoint set; the
existing single-endpoint public issue id is preserved, and multi-endpoint
identities hash the structured ordered collection.
lavigne958
lavigne958 previously approved these changes Aug 28, 2026
@NouemanKHAL

Copy link
Copy Markdown
Member

Reviewed the change. It's well-built overall: limiter state is read before the finally reset, the inner try/except isolates reporting failures from limiter cleanup, issue-ID determinism is sound, and division-by-zero is impossible (reached_limit implies count > limit >= 1). Two things worth considering before merge:

1. issue_type as a required param is backward-incompatible for report_issue.
report_issue is a released public AgentCheck method (shipped in datadog_checks_base 37.41.0 via #24016). Making issue_type required with no default means any existing caller that omits it now raises ValueError("Issue Type is required") — keyword or positional. There are no production callers in integrations-core today, but the API is public and could be used in extras/marketplace/internal repos. The payload proto also declares issue_type as omitempty (optional on the wire), so requiring it in Python is stricter than the transport. Consider defaulting it (e.g. derive from issue_name, or allow None) to preserve compatibility.

2. Isolated checks are silently out of scope.
The _on_metric_limit_state call sits in the non-isolation else branch, so OpenMetrics checks running with process_isolation never surface this issue (the parent's metric_limiter.count stays 0). The placement is correct — running it under isolation would only fire spurious resolve_issue RPCs — but the limitation is undocumented. A one-line note in the PR description would help.

Minor (non-blocking): observed_count is the check-wide limiter total, so it includes telemetry contexts when telemetry: true and covers all endpoints on multi-endpoint checks. The description wording ("These totals cover the complete check run") already accounts for this, so no change needed; just flagging that the remediation summary says "this endpoint" (singular) which reads slightly off for multi-endpoint checks like Cilium.

The limiter's observed count covers the complete check run across all endpoints and telemetry contexts, so the remediation summary now addresses the check instance instead of a single endpoint.
@temporal-github-worker-1
temporal-github-worker-1 Bot dismissed lavigne958’s stale review August 31, 2026 10:47

Review from lavigne958 is dismissed. Related teams and files:

  • container-integrations
    • datadog_checks_base/datadog_checks/base/checks/openmetrics/metric_limit_issue.py
The isolated child process reconstructs the check from serialized
configuration alone, so it must derive its over-limit behavior from
config rather than parent-object state. IsolatedMetricLimitOpenMetricsCheck
submits a fixed number of contexts, and test_isolated_check_reports_metric_limit_issue
verifies the child reports the issue, the replay bridge forwards it to the
parent Agent stub, and the parent's own limiter stays untouched.
@dd-octo-sts

dd-octo-sts Bot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Validation Report

All 21 validations passed.

Show details
Validation Description Status
agent-reqs Verify check versions match the Agent requirements file
ci Validate CI configuration and code coverage settings
codeowners Validate every integration has a CODEOWNERS entry
config Validate default configuration files against spec.yaml
dep Verify dependency pins are consistent and Agent-compatible
http Validate integrations use the HTTP wrapper correctly
imports Validate check imports do not use deprecated modules
integration-style Validate check code style conventions
jmx-metrics Validate JMX metrics definition files and config
labeler Validate PR labeler config matches integration directories
legacy-signature Validate no integration uses the legacy Agent check signature
license-headers Validate Python files have proper license headers
licenses Validate third-party license attribution list
metadata Validate metadata.csv metric definitions
models Validate configuration data models match spec.yaml
openmetrics Validate OpenMetrics integrations disable the metric limit
package Validate Python package metadata and naming
qa-label Validate the pull request declares whether it needs QA for the next Agent release
readmes Validate README files have required sections
saved-views Validate saved view JSON file structure and fields
version Validate version consistency between package and changelog

View full run

@nubtron

nubtron commented Aug 31, 2026

Copy link
Copy Markdown
Collaborator Author

@NouemanKHAL thanks for the review!

  1. issue_type as a required param is backward-incompatible for report_issue.

There are no other callers of issue_type in marketplace or extras, and we do want future callers to use issue_type. IMO the benefit or making it mandatory (no future accidental omission) outweighs the cost (unlikely custom check use), what do you think?

  1. Isolated checks are silently out of scope.
    The _on_metric_limit_state call sits in the non-isolation else branch, so OpenMetrics checks running with process_isolation never surface this issue

I'm not sure this is right , since the isolation branch calls the non-isolated branch in a new process. Added a test to verify that: d259c32

just flagging that the remediation summary says "this endpoint" (singular) which reads slightly off for multi-endpoint checks like Cilium.

This is a good point, I changed the wording so that it says "instance" instead of "endpoint".

@NouemanKHAL

Copy link
Copy Markdown
Member

Sounds good to me, I wasn't so sure about the 2nd one, figured to share the claude finding as you'd have more context.

@nubtron

nubtron commented Aug 31, 2026

Copy link
Copy Markdown
Collaborator Author

figured to share the claude finding as you'd have more context.

@NouemanKHAL it did expose a testing gap! Now we have have a proper process_isolation test!

@nubtron
nubtron added this pull request to the merge queue Aug 31, 2026
Merged via the queue into master with commit ded1457 Aug 31, 2026
658 of 662 checks passed
@nubtron
nubtron deleted the nubtron/ai-7012-openmetrics-dropped-config branch August 31, 2026 13:12
@dd-octo-sts dd-octo-sts Bot added this to the 7.83.0 milestone Aug 31, 2026
github-actions Bot pushed a commit that referenced this pull request Aug 31, 2026
#24819)

* Prototype OpenMetrics metric limit health issue

* Add changelog for OpenMetrics health issue prototype

* Refine OpenMetrics metric limit issue ownership

* Scope OpenMetrics health issue IDs to host and check

* Simplify OpenMetrics metric limit cleanup

* Simplify metric limit hook ordering

* Clarify OpenMetrics metric limit reporting

* Simplify OpenMetrics metric limit remediation

* Tighten OpenMetrics remediation wording

* Resolve OpenMetrics health issues on clean runs

* Report OpenMetrics health issues for any truncation

* Route OpenMetrics health issues to Integrations

* Remove Markdown backticks from remediation text

The Fleet UI renders remediation step text as plain text, so the backticks
around config option names were shown literally to customers. Match the
plain-text convention used by other Agent Health producers.

* Add IssueType to Agent Health reports

* Align OpenMetrics IssueType with IssueName

* Replace legacy flag with explicit metric filter config

The MetricLimitIssueReporter carried a boolean `legacy` flag whose only
effect was selecting between the v1 (`metrics` / `ignore_metrics`) and
v2 (`metrics` / `exclude_metrics`) config option names in remediation
text. Pass the actual option string instead so the reporter no longer
knows about OpenMetrics flavors and the call sites document themselves.

* Clarify debug_metrics.metric_contexts remediation as nested config

The Fleet UI renders Agent Health remediation text as plain text, so the
dotted form 'debug_metrics.metric_contexts: true' reads as a single literal
key. The check parses instance.get('debug_metrics', {}).get('metric_contexts'),
so a dotted top-level key would never match and no telemetry would be emitted.

Describe the nesting in prose ('set metric_contexts to true under the
debug_metrics section') and spell out both emitted metric names in full
instead of the orphaned '.limit'. Add assertions locking in the corrected
wording.

* Clarify Agent Health issue name test

* Remove redundant OpenMetrics reporter ownership test

* Report OpenMetrics metric-limit drops against configured scraper endpoints

The V2 metric-limit callback read openmetrics_endpoint from the raw instance,
which stays absent for integrations such as Cilium that synthesize scraper
configs from agent_endpoint/operator_endpoint, so drops were never reported.
Pass the actual configured scraper endpoint keys from self.scrapers instead,
and have V1 pass its one effective prometheus_url.

MetricLimitIssueReporter.handle now accepts a collection of endpoints and
normalizes it to a deterministic, de-duplicated, sorted tuple, discarding
empty values. One aggregate issue is reported for the whole run since the
limiter state cannot attribute drops to a single scraper. The machine-readable
extra['endpoints'] list and the issue identity reflect the endpoint set; the
existing single-endpoint public issue id is preserved, and multi-endpoint
identities hash the structured ordered collection.

* Skip metric limit handling in isolated parent

* Simplify OpenMetrics multi-endpoint issue reporting

* Clarify OpenMetrics filter option display text

* Test remediation copy in its owning package

* Clarify generic metric limit test fixture

* Clarify OpenMetrics endpoint test setup

* Clarify check-wide metric limit remediation

The limiter's observed count covers the complete check run across all endpoints and telemetry contexts, so the remediation summary now addresses the check instance instead of a single endpoint.

* Add end-to-end process isolation test for metric limit issue

The isolated child process reconstructs the check from serialized
configuration alone, so it must derive its over-limit behavior from
config rather than parent-object state. IsolatedMetricLimitOpenMetricsCheck
submits a fixed number of contexts, and test_isolated_check_reports_metric_limit_issue
verifies the child reports the issue, the replay bridge forwards it to the
parent Agent stub, and the parent's own limiter stays untouched. ded1457
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants